#include "dso/DsoLoader.hpp"

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <dlfcn.h>
#include <link.h>

namespace rewine
{

static char *g_LastError;

static int
rewine_dlclose(void *handle)
{
    dlerror();
    int ret = dlclose(handle);
    g_LastError = dlerror();
    return ret;
}

static void
dl_handle_deleter(dl_handle_t *pHandle)
{
    if (pHandle) {
        rewine_dlclose(*pHandle);
        delete pHandle;
    }
}

std::shared_ptr<dl_handle_t>
rewine_dlopen(const char *file, bool bLazy, bool bDeepbind)
{
    dlerror();
    int mode = RTLD_LOCAL;
    if (bLazy) mode |= RTLD_LAZY;
    if (bDeepbind) mode |= RTLD_DEEPBIND;
    auto handle = dlopen(file, mode);
    g_LastError = dlerror();
    if (!handle) {
        return nullptr;
    }
    std::shared_ptr<dl_handle_t> pHandle;
    pHandle.reset(new dl_handle_t(handle), &dl_handle_deleter);
    return std::move(pHandle);
}

void *
rewine_dlsym(dl_handle_t handle, const char *name)
{
    dlerror();
    void *ret = dlsym(handle, name);
    g_LastError = dlerror();
    return ret;
}

void *
rewine_dlinfo_map(dl_handle_t handle) {
    dlerror();
    struct link_map *map;
    int ret = dlinfo(handle, RTLD_DI_LINKMAP, &map);
    g_LastError = dlerror();
    return (!ret) ? map : nullptr;
}

const char *
rewine_dlerror()
{
    return g_LastError;
}

} // namespace rewine
